def invert(simbol)
  (simbol == "+" || simbol == false) ? "-" : "+";
end

class Numeric
  def mod
    (self < 0) ? 0 - self : self;
  end
end


class Person
  attr_reader :name, :time_to_cross;
  attr_accessor :with_flashlight;
  
  def initialize(name, time_to_cross, with_flashlight=false)
    @name, @time_to_cross = name.to_s, time_to_cross;
    @with_flashlight = !!with_flashlight;
  end

  def flashlight?
    @with_flashlight;
  end

  alias_method :to_s, :name;

  def to_sym
    to_s.downcase.to_sym;
  end
  
end

class Bridge
  attr_reader :time_spent, :destination, :stopped, :people;
  
  def initialize(people)
    @max_people = people.delete(:max_people) || nil;
    @people = get_people(people);
    @time_spent = 0;
    @destination = [];
    @stopped = @people;
  end
  
  def go(*people)
    move_people(people, :to)
  end
  
  def back(*people)
    move_people(people, :back)
  end
  
  def flashlight_with(person=nil)
    if person
      @people.map! do |person|
        person.with_flashlight = (person.name.to_sym == name);
        person;
      end
    else
      @people.detect{|person| person.flashlight?}
    end
  end
  
  private
  def get_people(people)
    if people.kind_of?(Array)
      @people.select{|person| people.include?(person.to_sym)}
    else
      flash = people.delete(:flashlight);
      people[flash] = [people[flash], true] if flash;
      people.map{|name, time| Person.new(name, *[time].flatten);}
    end
  end
  
  def move_people(people, direction=:to)
    raise "Can't carry more people than it suports" if @max_people && people.size > @max_people;
    people = get_people(people);
    @time_spent = people.inject(@time_spent){|time_spent, person| time_spent + person.time_to_cross};
    source = (direction == :to) ? @stopped : @destination;
    raise "Can't carry person from anywhere" unless people.all?{|person| source.include?(person)}
    raise "At least one person must have a flashlight" unless people.any?{|person| person.flashlight?}
    op = invert(direction == :to);
    @destination = @destination.send(op, people);
    @stopped = @stopped.send(invert(op), people);
  end

end

def test(bridge, max=60)
  time = bridge.time_spent;
  diff = max - time;
  puts "Maximum time exceeded!" if diff < 0;
  word = (diff < 0) ? "exceeded" : "still have";
  puts "You used #{time}, #{word} #{diff.mod} from #{max} max";
  order = lambda{|people| people.map{|person| ((person.flashlight?) ? "*" : "") + person.name}.join(", ")}
  puts "#{order.call(bridge.stopped)} -> #{order.call(bridge.destination)}";
  puts "Flashlight with #{bridge.flashlight_with.to_s}";
  puts
end

puts(lambda{

# Buzz:    5 minutes
# Woody:  10 minutes
# Rex: 	  20 minutes
# Hamm:   25 minutes

bridge = Bridge.new :flashlight => :rex, :max_people => 2, :buzz => 5, :woody => 10, :rex => 20, :hamm => 25;
test bridge

bridge.go(:rex, :hamm)
test bridge
bridge.back(:buzz)
test bridge
bridge.go(:hamm, :buzz)
test bridge

""}.call)